// 定义常量：promise的三种状态
const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"
class MPromise {
    // 定义realOnFulfilled和realOnRejected两个函数的数组
    ONFULFILLED_CALLBACK_LIST = []
    ONREJECTED_CALLBACK_LIST = []

    // 定义私有变量-status用来获取和设置status
    _status = PENDING

    // promise构造函数的入参（是一个函数，并且接收两个参数resolve，reject）
    // 在new Promise的时候，就要执行这个函数，并且有任何的错误都要reject出去
    constructor(fn) {
        // 设置promise的初始状态
        this.status = PENDING
        this.value = null
        this.reason = null
        try {
            // 立即执行fn，如果不bind改变this指向，那么就会丢失原有的this环境，可能会拿不到value，reason的值
            // 如果是通过箭头函数赋值，其指向就容易发生错误
            fn(this.resolve.bind(this), this.reject.bind(this))
        } catch (error) {
            this.reject(error)
        }
    }

    // 使用_status而不用status的原因就是防止死循环
    get status() {
        return this._status
    }

    // 单一职责：当status改变时执行回调
    set status(newStatus) {
        this._status = newStatus
        switch (this._status) {
            case FULFILLED: {
                this.ONFULFILLED_CALLBACK_LIST.forEach((callback) => {
                    callback(this.value)
                })
                break
            }
            case REJECTED: {
                this.ONREJECTED_CALLBACK_LIST.forEach((callback) => {
                    callback(this.reason)
                })
                break
            }
        }
    }

    // resolve 和 reject（单一职责）：改变promise的状态（并且只有resolve和reject改变状态）
    resolve(value) {
        // 只有promise的状态是pending时，才可以改变promise的fulfilled状态（单项的状态流转pending => fulfilled）
        if (this.status == PENDING) {
            this.status = FULFILLED
            this.value = value
        }
    }

    reject(reason) {
        // 只有promise的状态是pending时，才可以改变promise的rejected状态(单向的状态流转pending => rejected)
        if (this.status == PENDING) {
            this.status = REJECTED
            this.reason = reason
        }
    }

    // 接收两个参数，并且必须是函数，如果不是那就忽略
    then(onFulfilled, onRejected) {
        // 这两个函数的作用只是把值进行处理之后提交给resolvePromise函数来决定promise2的状态和内容
        const realOnFulfilled = this.isFunction(onFulfilled)
            ? onFulfilled
            : (value) => {
                  return value
              }
        const realOnRejected = this.isFunction(onRejected)
            ? onRejected
            : (reason) => {
                  // 使用throw是因为在接下来的环节会通过try...catch来捕获
                  throw reason
              }
        // 整体的返回值是一个新的promise
        const promise2 = new MPromise((resolve, reject) => {
            // 当执行回调时，如果有错误进行捕获之后，通过reject抛出去
            const fulfilledMicrotask = () => {
                // 添加到微任务队列
                queueMicrotask(() => {
                    try {
                        const x = realOnFulfilled(this.value)
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            }

            const rejectedMicrotask = () => {
                // 添加到微任务队列
                queueMicrotask(() => {
                    try {
                        const x = realOnRejected(this.reason)
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            }

            switch (this.status) {
                // 如果status是fulfilled，就执行realOnFulfilled
                case FULFILLED: {
                    fulfilledMicrotask()
                    break
                }
                // 如果status是rejected，就执行realOnFulfilled
                case REJECTED: {
                    rejectedMicrotask()
                    break
                }
                // 当状态是pending状态时，将realOnFulfilled和realOnRejected函数添加到定义好的数组中去
                case PENDING: {
                    this.ONFULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
                    this.ONREJECTED_CALLBACK_LIST.push(rejectedMicrotask)
                }
            }
        })
        return promise2
    }

    catch(onRejected) {
        return this.then(null, onRejected)
    }

    resolvePromise(promise2, x, resolve, reject) {
        // 判定结果和返回的promise2是不是同一个对象，虽然不太理解什么样的情况才会出现这种操作
        if (promise2 === x) {
            return reject(
                new TypeError("the promise2 and the return value are the same"),
            )
        }
        // 检测上一个promise返回的结果是否是promise，如果不是的话，就取该结果的值，如果值依旧是promise，那么就往下递归
        // 如果返回的promise是Rejected结果的，那么就直接拒绝该状态
        if (x instanceof MPromise) {
            queueMicrotask(() => {
                x.then((y) => {
                    this.resolvePromise(promise2, y, resolve, reject)
                }, reject)
            })
        }
        // 返回值如果是对象或者函数的情况
        else if (typeof x === "object" || this.isFunction(x)) {
            // 如果x是null的时候
            if (x === null) {
                return resolve(x)
            }
            let then = null
            // 判断是一个thenable对象，是的话就取其then方法，不是就拒绝状态
            try {
                then = x.then
            } catch (error) {
                return reject(error)
            }
            // 检查then属性是否是方法
            if (this.isFunction(then)) {
                let called = false
                try {
                    // 相当于让x结果执行一下then方法，目的就是为了取得value或者reason，和上面嵌套取值是同理的
                    then.call(
                        x,
                        (y) => {
                            // 使用called作为限制条件，限制只能执行一次
                            if (called) {
                                return
                            }
                            called = true
                            this.resolvePromise(promise2, y, resolve, reject)
                        },
                        (r) => {
                            if (called) {
                                return
                            }
                            called = true
                            reject(r)
                        },
                    )
                } catch (error) {
                    if (called) {
                        return
                    } else {
                        reject(error)
                    }
                }
            }
            // 如果x本身不符合thenable协议的话就直接返回x
            else {
                resolve(x)
            }
        }
        // 如果是基本数据类型，就直接返回即可
        else {
            resolve(x)
        }
    }

    isFunction(param) {
        return typeof param === "function"
    }

    static resolve(value) {
        if (value instanceof MPromise) {
            return value
        }
        return new MPromise((resolve, reject) => {
            resolve(value)
        })
    }

    static reject(reason) {
        return new MPromise((resolve, reject) => {
            reject(reason)
        })
    }
}
